﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Xml;
using log4net;
using Microsoft.Build.Evaluation;
using Truncation.Detector.Windows;
using Truncation.Detector.WPF.XamlDom;

namespace Truncation.Detector.WPF
{
    public class Entry : ProjectImpl
    {
        private static readonly ILog Log = LogManager.GetLogger("Main");
        private ISolutionImpl _solution = null;
        private readonly Report _report = new Report();
        private string _currentXaml = null;
        private Uri _baseFolder;
        public Entry(Project project)
            : base(project)
        {
            _baseFolder = new Uri(Environment.CurrentDirectory + Path.DirectorySeparatorChar);
        }

        public override void Run(ISolutionImpl solution)
        {
            Log.InfoFormat("--> Start to parse WPF project: {0}", FullPath);
            this._solution = solution;

            foreach (var page in MsBuildProject.GetItems("Page"))
            {
                var type = page.GetMetadataValue("SubType");
                if ("Designer".Equals(type, StringComparison.OrdinalIgnoreCase))
                {
                    string include = Path.Combine(MsBuildProject.DirectoryPath, page.EvaluatedInclude);
                    Log.DebugFormat("Check xaml file: {0}", include);
                    Parse(include);
                }
            }
            _report.Output();
        }

        private void Parse(string fileName)
        {
            Summary.Instance.Counts ++;
            _currentXaml = _baseFolder.MakeRelativeUri(new Uri(fileName)).ToString();
            //var typeFinder = new CustomTypeFinder();
            using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var reader = new XmlTextReader(stream))
            {
                //var settings = new XamlParserSettings { TypeFinder = typeFinder };
                //var doc = XamlParser.Parse(reader, settings);
                var nameSpaceDictionary = new Dictionary<string, string>();
                var doc = new PositionXmlDocument();
                doc.Load(reader);
                TraverseXml(doc.DocumentElement, nameSpaceDictionary);
            }
        }

        protected virtual void TraverseXml(XmlNode node, Dictionary<string, string> namespaces)
        {
            Dictionary<string, string> nameSpaceDictionary = namespaces.ToDictionary(s => s.Key,
                s => s.Value);
            if (node.NodeType == XmlNodeType.Element && node.Attributes != null)
            {
                foreach (XmlAttribute attribute in node.Attributes)
                {
                    if (attribute.Name.StartsWith("xmlns"))
                    {
                        nameSpaceDictionary.Add(attribute.Name, attribute.Value);
                    }
                }
            }
            foreach (XmlNode x in node.ChildNodes)
            {
                if (x.NodeType == XmlNodeType.Element)
                {
                    var element = (PositionXmlElement)x;
                    if (element.Name.ToLowerInvariant() == "button")
                    {
                        MeasureButton(element, nameSpaceDictionary);
                        continue;
                    }
                    else if (element.Name.ToLowerInvariant() == "textblock")
                    {
                        continue;
                    }
                    TraverseXml(element, nameSpaceDictionary);
                }
            }
            var next = node.NextSibling;
            if (next != null)
                TraverseXml(next, namespaces);
        }
        private void MeasureButton(PositionXmlElement element, Dictionary<string, string> namespaces)
        {
            //var typeFinder = new CustomTypeFinder();
            //var settings = new XamlParserSettings { TypeFinder = typeFinder };
            var clrNamespaces = new Dictionary<string, KeyValuePair<ProjectImpl, string>>();
            foreach (var ns in namespaces)
            {
                if (!ns.Value.StartsWith("clr-namespace", StringComparison.OrdinalIgnoreCase))
                    element.SetAttribute(ns.Key, ns.Value);
                else
                {
                    var clrNamespace = ns.Value.Split(new[] { ':', ';', '=' });
                    ProjectImpl prj = this;
                    string baseNameSpace = clrNamespace[1];
                    if (clrNamespace.Length == 4)
                    {
                        prj = _solution.GetProject(clrNamespace[3]);
                    }
                    clrNamespaces.Add(ns.Key.Substring(XamlConstants.Xmlns.Length + 1), new KeyValuePair<ProjectImpl, string>(prj, baseNameSpace));
                }
            }

            foreach (var lang in Translation.SupportedLangs)
            {
                var text = ReplaceContent(element.OuterXml, lang, clrNamespaces);
                var appXamlXml = XmlReader.Create(new StringReader(text));
                var parsed = XamlParser.Parse(appXamlXml, new XamlParserSettings());
                var btnControl = (Button)parsed.RootInstance;
                double width = 0;
                double height = 0;
                if (!Double.IsNaN(btnControl.Width))
                {
                    width = btnControl.Width - btnControl.BorderThickness.Left - btnControl.BorderThickness.Right - btnControl.Padding.Left - btnControl.Padding.Right;
                }
                if (!Double.IsNaN(btnControl.Height))
                {
                    height = btnControl.Height - btnControl.BorderThickness.Top - btnControl.BorderThickness.Bottom -
                             btnControl.Padding.Top - btnControl.Padding.Bottom;
                }
                if (width > 0 && btnControl.Content is String)
                {
                    string content = (string)btnControl.Content;
                    var formattedText = new FormattedText(
                                            content,
                                            CultureInfo.CurrentUICulture,
                                            FlowDirection.LeftToRight,
                                            new Typeface(btnControl.FontFamily, btnControl.FontStyle, btnControl.FontWeight, btnControl.FontStretch),
                                            btnControl.FontSize,
                                            Brushes.Black);
                    if (formattedText.Width > width || (!Double.IsNaN(btnControl.Height) && formattedText.Height > height))
                    {
                        if (0 == _report.AddResult(_currentXaml, element.Name, lang, element.LineNumber))
                        {
                            Summary.Instance.Items++;
                            Summary.Instance.Warnings++;
                        }
                    }
                }
                else if (btnControl.Content is UIElement)
                {
                    var content = (UIElement)btnControl.Content;
                }
                
                
            }


            //parsed.RootInstance
        }

        private string ReplaceContent(string text, string lang, Dictionary<string, KeyValuePair<ProjectImpl, string>> clrNamespaces)
        {
            const string pattern = @"(?<type>(Content|Text))\s*=\s*""(?<content>.*?)""";
            string ret = new Regex(pattern).Replace(text,
                match =>
                {
                    string valueText = match.Groups["content"].Value;
                    string typeText = match.Groups["type"].Value;
                    if (String.IsNullOrWhiteSpace(valueText))
                    {
                        return match.Groups[0].Value;
                    }
                    if (valueText.StartsWith("{", StringComparison.Ordinal) &&
                        !valueText.StartsWith("{}", StringComparison.Ordinal))
                    {
                        var tokens = MarkupExtensionTokenizer.Tokenize(valueText);
                        if (tokens.Count >= 3 && tokens[1].Value == "x:Static" && tokens[2].Kind == MarkupExtensionTokenKind.String)
                        {
                            string value = tokens[2].Value;
                            var nsValue = value.Split(':');
                            KeyValuePair<ProjectImpl, string> resourceKey;
                            if (clrNamespaces.TryGetValue(nsValue[0], out resourceKey))
                            {
                                var path = resourceKey.Key.MsBuildProject.DirectoryPath;
                                if (String.IsNullOrEmpty(path))
                                    return match.Groups[0].Value;
                                var resources = nsValue[1].Split('.');
                                path = Path.Combine(path, resourceKey.Value.Substring(resourceKey.Key.AssemblyName.Length + 1));
                                for (int i = 0; i < resources.Length - 1; i++)
                                {
                                    path = Path.Combine(path, resources[i]);
                                }
                                string key = resources[resources.Length - 1];
                                ResourceData reader = resourceKey.Key.GetResource(path);
                                if (reader != null)
                                {
                                    valueText = reader.GetValue(key, lang);
                                }
                            }
                        }
                    }
                    return String.Format("{0}=\"{1}\"", typeText, valueText);
                });
            return ret;
        }

    }
}